#ifndef TASK_H
#define TASK_H
#include "system.h"

/// kmcguire- not sure if address translation should be in task.h; but they at least do not hurt anything right now.
// ADDRESS TRANSLATION //
/* segment:offset pair */
typedef uint32_t FARPTR;
typedef uint32_t addr_t;
/* Make a FARPTR from a segment and an offset */
#define MK_FP(seg, off)    ((FARPTR) (((uint32_t) (seg) << 16) | (uint16_t) (off)))
/* Extract the segment part of a FARPTR */
#define FP_SEG(fp)        (((FARPTR) fp) >> 16)
/* Extract the offset part of a FARPTR */
#define FP_OFF(fp)        (((FARPTR) fp) & 0xffff)
/* Convert a segment:offset pair to a linear address */
#define FP_TO_LINEAR(seg, off) ((void*) ((((uint16_t) (seg)) << 4) + ((uint16_t) (off))))

///////////////////////////////////////////////////////////////////////////////////////////
#define SCHED_KERNEL_CODEDESC 	0x08
#define SCHED_KERNEL_DATADESC 	0x10
#define SCHED_KERNEL_STACKDESC 	0x10

#define SCHED_EFLAGS32			0x00000200
#define SCHED_EFLAGS16			0x00020202

// stack size specifier in pages.
typedef unsigned long SCHED_STACKSIZE;
// process load helper callback
typedef int(*SCHED_PLH)(void);

typedef unsigned long 	CR3;
// type definition for the context id.
typedef CR3 		CONTEXTID;
// type defintion for the process identifier.
typedef long	 	PROCESSID;
// type defintion for a thread identifier.
typedef long	 	THREADID;
// type defintion for a thread priority value.
typedef unsigned char 	SCHED_PRI;
typedef unsigned long 	ENTRYADDR;
typedef unsigned long 	STACKADDR;
typedef unsigned short 	SEGDESCRIPTOR;
typedef unsigned long 	EFLAGS;
typedef unsigned long	THREADFLAGS;
typedef unsigned long	MEDADDR;

struct sched_threadRegisters
{
	unsigned long	eax, ebx, ecx, edx, esi, edi, ebp;
};
// Holds a description for the creation of a thread.
struct sched_threadDescription
{
	CONTEXTID 			contextId;
	PROCESSID 			processId;
	ENTRYADDR	 		entry;
	SCHED_PRI			priority;
	STACKADDR			kstack;
	STACKADDR			ustack;
	SEGDESCRIPTOR		cs;
	SEGDESCRIPTOR		ds;
	EFLAGS			eflags;
	sched_threadRegisters	registers;
	MEDADDR			medaddr;
};

// This is the state of any thread, and is updated as needed.
struct sched_thread
{
	STACKADDR	 	ustack;
	STACKADDR	 	kstack;
	CR3			cr3;
	PROCESSID 		processId;
	THREADID		threadId;
	SCHED_PRI		priority;
	THREADFLAGS		flags;
	sched_thread	*next, *prev;
};

struct sched_stackTemplate
{
	unsigned long 	gs, fs, es, ds;						// added: pcmattman descriptor save in irq.inc
	unsigned long 	edi, esi, ebp, esp, ebx, edx, ecx, eax;
	unsigned long 	intnum, errcode;					// added: pcmattman intnum and errcode passed via stack.
	unsigned long 	eip, cs, eflags;
};

#define SCHED_PRICNT 10

// adjustable array minimum free slots.
#define SCHED_THREADARRAY_FREE_MIN		20
// adjustable array maximum free slots.
#define SCHED_THREADARRAY_FREE_MAX		40

#define SCHED_THREAD_KILLED			0x1

struct sched_pri
{
	unsigned long cnt;
	sched_thread *active;
};

extern sched_thread *____sched_curthread;

/// @old functionality
/// -- The old functionality has been adapted to work properly with the new implementation.
// macro for stub for existing kernel intrastructure intergration
#define initmt sched_initialize
#define resched sched_switch
// function to control multi-tasking state (old)
bool mtenabled();
void mtdisable();
void mtenable();
// set the current process' registers (old)
int SetRegs(int pid, struct regs *r);
// kill the current process using the PID (old)
int kill(int pid);
// gets the current PID (old function)
int currpid();

/// @common initialization
int sched_initialize();

/// @common functionality
// create a thread. (kernel thread use zero for processID) (uses a _cdelc calling convention)
THREADID sched_create_thread( sched_threadDescription *desc, unsigned long argCount, ...);
/// performs a thread switch only if a certain amount of block ticks have passed.
void sched_switch();
/// performs a thread switch regardless of how many clock ticks have passed.
void sched_switch_core();
#endif

